// Test for the specific scenario mentioned in the issue:
// "DELETE http://10.160.43.82:7777/api/data/42 404 (Not Found)"
// This test ensures the delete endpoint properly handles non-existent IDs

use serde_json::Value;
use std::sync::Arc;
use std::time::Duration;
use tempfile::TempDir;
use tokio::time::timeout;

use axum::{
    extract::DefaultBodyLimit,
    http::Method,
    routing::{delete, get, post, put},
    Router,
};
use rdm_rust::config::AppConfig;
use rdm_rust::database::connection::establish_connection_pool;
use rdm_rust::handlers::{auth, data};
use rdm_rust::services::{AuthService, DataService, DvcService, StorageService};
use rdm_rust::AppState;
use tower::ServiceBuilder;
use tower_http::{
    cors::{Any, CorsLayer},
    trace::TraceLayer,
};

// Test configuration
const TEST_SERVER_PORT: u16 = 0; // Use random available port
const TEST_TIMEOUT: Duration = Duration::from_secs(30);

struct TestServer {
    base_url: String,
    client: reqwest::Client,
}

impl TestServer {
    async fn new() -> Self {
        // Create temporary directory for test storage
        let temp_dir = TempDir::new().expect("Failed to create temp dir");

        // Load test configuration
        let mut config = AppConfig::from_env().expect("Failed to load config");
        config.server.port = TEST_SERVER_PORT;
        config.dvc.storage_path = temp_dir.path().to_string_lossy().to_string();

        // Initialize database
        let db_pool = establish_connection_pool(&config.database.url)
            .await
            .expect("Failed to establish database connection");

        // Initialize services
        let auth_service = Arc::new(AuthService::new(db_pool.clone(), config.clone()));
        let data_service = Arc::new(DataService::new(db_pool.clone()));
        let storage_service = Arc::new(StorageService::new(config.dvc.storage_path.clone()));
        let dvc_service = Arc::new(DvcService::new(db_pool.clone()));

        let app_state = AppState {
            auth_service,
            data_service,
            storage_service,
            dvc_service,
        };

        // Build application router (same as main.rs)
        let public_routes = Router::new()
            .route("/api/register", post(auth::register))
            .route("/api/register-admin", post(auth::register_admin))
            .route("/api/login", post(auth::login))
            .route("/api/data/public", get(data::list_public_data_items));

        let protected_routes = Router::new()
            .route("/api/user-info", get(auth::user_info))
            .route("/api/data", get(data::list_data_items))
            .route("/api/data/upload", post(data::upload_data))
            .route("/api/data/:id", get(data::get_data_item))
            .route("/api/data/:id", delete(data::delete_data_item))
            .route("/api/data/:id/download", get(data::download_data_file))
            .route("/api/data/:id/dvc", get(data::download_dvc_file))
            .route("/api/data/:id/dvc/content", get(data::get_dvc_content))
            .route("/api/data/:id/metadata", put(data::upload_metadata))
            .route_layer(axum::middleware::from_fn_with_state(
                app_state.clone(),
                rdm_rust::middleware::auth::auth_middleware,
            ));

        let app = Router::new()
            .merge(public_routes)
            .merge(protected_routes)
            .layer(
                ServiceBuilder::new()
                    .layer(TraceLayer::new_for_http())
                    .layer(
                        CorsLayer::new()
                            .allow_origin(Any)
                            .allow_methods([
                                Method::GET,
                                Method::POST,
                                Method::PUT,
                                Method::DELETE,
                                Method::OPTIONS,
                            ])
                            .allow_headers(Any),
                    )
                    .layer(DefaultBodyLimit::max(100 * 1024 * 1024)), // 100MB max file size
            )
            .with_state(app_state);

        // Start server on random port
        let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
            .await
            .expect("Failed to bind to address");
        let port = listener
            .local_addr()
            .expect("Failed to get local address")
            .port();

        // Spawn server task
        tokio::spawn(async move {
            axum::serve(listener, app)
                .await
                .expect("Failed to serve server");
        });

        // Give server time to start
        tokio::time::sleep(Duration::from_millis(100)).await;

        let base_url = format!("http://127.0.0.1:{}", port);
        let client = reqwest::Client::builder()
            .timeout(TEST_TIMEOUT)
            .build()
            .expect("Failed to create HTTP client");

        Self { base_url, client }
    }

    async fn wait_for_server(&self) -> Result<(), Box<dyn std::error::Error>> {
        let health_url = format!("{}/api/data/public", self.base_url);

        for _ in 0..10 {
            match timeout(Duration::from_secs(1), self.client.get(&health_url).send()).await {
                Ok(Ok(response)) if response.status().is_success() => return Ok(()),
                Ok(_) => (),
                Err(_) => (),
            }
            tokio::time::sleep(Duration::from_millis(100)).await;
        }

        Err("Server failed to start".into())
    }

    async fn register_and_login(&self, email: &str, password: &str) -> String {
        // Register user
        let register_response = self
            .client
            .post(format!("{}/api/register", self.base_url))
            .json(&serde_json::json!({
                "email": email,
                "password": password
            }))
            .send()
            .await
            .expect("Failed to send register request");

        assert!(
            register_response.status().is_success(),
            "User registration should succeed"
        );

        // Login to get auth token
        let login_response = self
            .client
            .post(format!("{}/api/login", self.base_url))
            .json(&serde_json::json!({
                "email": email,
                "password": password
            }))
            .send()
            .await
            .expect("Failed to send login request");

        assert_eq!(login_response.status(), 200, "Login should succeed");

        let login_body: Value = login_response
            .json()
            .await
            .expect("Failed to parse login response");
        let auth_token = login_body
            .get("access_token")
            .or_else(|| login_body.get("token"))
            .and_then(|t| t.as_str())
            .expect("Login response should contain token");

        auth_token.to_string()
    }
}

#[tokio::test]
async fn test_specific_delete_id_404_scenario() {
    let server = TestServer::new().await;
    server
        .wait_for_server()
        .await
        .expect("Test server failed to start");

    println!("Test server running on: {}", server.base_url);

    // Setup: Register and login user
    let test_email = format!(
        "id404test{}@hillstonenet.com",
        chrono::Utc::now().timestamp_nanos_opt().unwrap_or(0)
    );
    let test_password = "testpassword123";
    let auth_token = server.register_and_login(&test_email, test_password).await;

    // Test the exact scenario from the error message: DELETE /api/data/42
    let problematic_id = 42i64;

    println!(
        "Testing DELETE /api/data/{} (the exact scenario from the error)",
        problematic_id
    );

    let delete_response = server
        .client
        .delete(format!("{}/api/data/{}", server.base_url, problematic_id))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .expect("Failed to send delete request for ID 42");

    // The expected behavior: should return 404 Not Found
    assert_eq!(
        delete_response.status(),
        404,
        "DELETE /api/data/42 should return 404 Not Found"
    );

    // Verify the error response contains appropriate information
    let error_body: Value = delete_response
        .json()
        .await
        .expect("Failed to parse error response");
    let error_detail = error_body.get("detail").unwrap().as_str().unwrap();

    println!("Error response: {}", error_body);

    // The error should indicate that the data item was not found
    assert!(
        error_detail.contains("not found") || error_detail.contains("Data item not found"),
        "Error should indicate the item was not found"
    );

    println!("✓ DELETE /api/data/42 correctly returns 404 Not Found");

    // Additional test: Try other non-existent IDs to ensure consistent behavior
    let other_nonexistent_ids = vec![0, 1, 999, 1000, -1];

    for id in other_nonexistent_ids {
        let delete_response = server
            .client
            .delete(format!("{}/api/data/{}", server.base_url, id))
            .header("Authorization", format!("Bearer {}", auth_token))
            .send()
            .await
            .expect("Failed to send delete request");

        assert_eq!(
            delete_response.status(),
            404,
            "DELETE /api/data/{} should return 404",
            id
        );
    }

    println!("✓ All non-existent IDs correctly return 404");

    // Test: Verify that the dashboard scenario would work correctly
    // First, upload a file to simulate the dashboard having a real file
    use reqwest::multipart;

    let test_content = b"CTI Workshop Full Slides-Mitre ATT&CK.pdf content";
    let form = multipart::Form::new()
        .part(
            "files",
            multipart::Part::bytes(test_content.to_vec())
                .file_name("CTI Workshop Full Slides-Mitre ATT&CK.pdf")
                .mime_str("application/pdf")
                .expect("Invalid mime type"),
        )
        .text("project", "CTI Workshop")
        .text("source", "https://example.com/cti-workshop")
        .text(
            "description",
            "CTI Workshop Full Slides with Mitre ATT&CK framework",
        );

    let upload_response = server
        .client
        .post(format!("{}/api/data/upload", server.base_url))
        .header("Authorization", format!("Bearer {}", auth_token))
        .multipart(form)
        .send()
        .await
        .expect("Failed to upload test file");

    assert_eq!(upload_response.status(), 200, "File upload should succeed");

    let upload_body: Value = upload_response
        .json()
        .await
        .expect("Failed to parse upload response");
    let uploaded_items = upload_body.get("items").unwrap().as_array().unwrap();
    let uploaded_item = &uploaded_items[0];
    let real_item_id = uploaded_item.get("id").unwrap().as_i64().unwrap();

    println!("Uploaded real file with ID: {}", real_item_id);

    // Test: Delete the real file (should succeed)
    let delete_real_response = server
        .client
        .delete(format!("{}/api/data/{}", server.base_url, real_item_id))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .expect("Failed to delete real file");

    assert_eq!(
        delete_real_response.status(),
        204,
        "DELETE real file should return 204 No Content"
    );

    println!("✓ Real file deletion succeeded with 204");

    // Test: Try to delete the same real file again (should now return 404)
    let delete_again_response = server
        .client
        .delete(format!("{}/api/data/{}", server.base_url, real_item_id))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .expect("Failed to delete already deleted file");

    assert_eq!(
        delete_again_response.status(),
        404,
        "DELETE already deleted file should return 404"
    );

    println!("✓ Deleting already deleted file correctly returns 404");

    // Final verification: The dashboard scenario is now properly tested
    println!("✅ All scenarios tested successfully:");
    println!("   - DELETE /api/data/42 (non-existent) → 404 Not Found");
    println!(
        "   - DELETE /api/data/{} (exists) → 204 No Content",
        real_item_id
    );
    println!(
        "   - DELETE /api/data/{} (after deletion) → 404 Not Found",
        real_item_id
    );
    println!("   - DELETE /api/data/{{any_nonexistent}} → 404 Not Found");
}

#[tokio::test]
async fn test_delete_without_auth_id_42() {
    let server = TestServer::new().await;
    server
        .wait_for_server()
        .await
        .expect("Test server failed to start");

    // Test: Try to delete ID 42 without authentication
    let delete_response = server
        .client
        .delete(format!("{}/api/data/42", server.base_url))
        .send()
        .await
        .expect("Failed to send unauthenticated delete request");

    assert_eq!(
        delete_response.status(),
        401,
        "DELETE without auth should return 401 Unauthorized"
    );

    println!("✓ DELETE /api/data/42 without auth correctly returns 401");
}
